//
//  sierpinski_tetrahedron.cpp
//  GLDemo
//
//  Created by wneqi on 14-6-3.
//  Copyright (c) 2014年 xiewneqi. All rights reserved.
//

#include <iostream>

#ifdef __APPLE__
#include <GLUT/glut.h>
#define glGenVertexArrays glGenVertexArraysAPPLE
#define glBindVertexArray glBindVertexArrayAPPLE
#else
#include "GL/glew.h"
#include <GL/glut.h>
#endif

#include "sierpinski_tetrahedron.h"
#include "shader_initialize.h"
#include <cmath>

static GLuint vbos[1];
static GLuint vaos[1];

static int DivisionDepth = 3;
static int NumTetrahedron = pow (4, DivisionDepth);
static int NumTriangles = NumTetrahedron * 4;
static int NumVertices = 3 * NumTriangles;
Point3* points_;
Point3* colors_;
Point3 base_colors[4] =
{
    {1, 0, 0},
    {0, 1, 0},
    {0, 0, 1},
    {0, 0, 0}
};
int colorIndex;

void makeup_triangle (Point3 a, Point3 b, Point3 c)
{
    static int i = 0;
    points_[i] = a;
    colors_[i] = base_colors[colorIndex];
    i ++;
    points_[i] = b;
    colors_[i] = base_colors[colorIndex];
    i ++;
    points_[i] = c;
    colors_[i] = base_colors[colorIndex];
    i ++;
}

void make_tetrahedron (Point3 a, Point3 b, Point3 c, Point3 d)
{
    colorIndex = 0;
    makeup_triangle (a, b, c);
    colorIndex = 1;
    makeup_triangle (a, c, d);
    colorIndex = 2;
    makeup_triangle (a, d, b);
    colorIndex = 3;
    makeup_triangle (b, d, c);
}

void divide_tetrahedron (Point3 a, Point3 b, Point3 c, Point3 d, int k)
{
    if (k > 0)
    {
        Point3 ab = (a + b)/2.0;
        Point3 ac = (a + c)/2.0;
        Point3 ad = (a + d)/2.0;
        Point3 bc = (b + c)/2.0;
        Point3 cd = (c + d)/2.0;
        Point3 bd = (b + d)/2.0;
        divide_tetrahedron(a, ab, ad, ac, k-1);
        divide_tetrahedron(ab, b, bd, bc, k-1);
        divide_tetrahedron(c, ac, cd, bc, k-1);
        divide_tetrahedron(cd, ad, bd, d, k-1);
    }
    else
    {
        make_tetrahedron (a, b, c, d);
    }
}

void init_sierpinski_tetrahedron_main (int argc, char **argv)
{
    glutInit (&argc, argv);

    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize (800, 400);
    glutInitWindowPosition (100, 100);
    glutCreateWindow ("Hello, sierpinski tetrahedron");
    glutDisplayFunc (display_sierpinski_tetrahedron);
    init_sierpinski_tetrahedron_gl ();
    glutMainLoop ();
}

void init_sierpinski_tetrahedron_gl ()
{
    // 使用glClearDepth和glDepthFunc调整深度测试的原因：
    // depthBuffer中存储的应该是用齐次坐标规范化后的viewSpace.z值[-1,1]，但在这个例子中，传递给
    // vertex shader的objectSpace坐标被直接记为viewSpace坐标。opengl是右手坐标系，离
    // 镜头越远Z值越小，这正好和viewSpace中的Z值排布是相反的，因此要把深度测试方式调整为
    // GL_GEQUAL，让Z值大（既靠前面）的物体显示出来。
    glClearDepth (0);
    glEnable (GL_DEPTH_TEST);
    glDepthFunc (GL_GEQUAL);
    
    
    glClearColor (1, 1, 1, 1);
    
    points_ = new Point3 [NumVertices];
    colors_ = new Point3 [NumVertices];
    divide_tetrahedron (Point3 {-1, -1, -1},
                        Point3 {1, -1, -1},
                        Point3 {0, 1, -1},
                        Point3 {0, 0, 1},
                        DivisionDepth);
    
    GLuint shaderProgram = InitShader ("vbo_vao_vertex_shader.glsl", "vbo_vao_fragment_shader.glsl");
    glUseProgram (shaderProgram);
    GLuint posLoc = glGetAttribLocation (shaderProgram, "va_position");
    GLuint colorLoc = glGetAttribLocation (shaderProgram, "va_color");
    
    glGenBuffers (1, vbos);
    glGenVertexArrays (1, vaos);
    
    glBindBuffer (GL_ARRAY_BUFFER, vbos[0]);
    glBufferData (GL_ARRAY_BUFFER, sizeof (Point3) * NumVertices + sizeof (Point3) * NumVertices, NULL, GL_STATIC_DRAW);
    glBufferSubData (GL_ARRAY_BUFFER, 0, sizeof (Point3) * NumVertices, points_);
    glBufferSubData (GL_ARRAY_BUFFER, sizeof (Point3) * NumVertices, sizeof (Point3) * NumVertices, colors_);
    
    glBindVertexArray (vaos[0]);
    glEnableVertexAttribArray (posLoc);
    glEnableVertexAttribArray (colorLoc);
    glBindBuffer (GL_ARRAY_BUFFER, vbos[0]);
    glVertexAttribPointer (posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glVertexAttribPointer (colorLoc, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(sizeof (Point3) * NumVertices));
}
void display_sierpinski_tetrahedron ()
{
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBindVertexArray (vaos[0]);
    // glDrawArrays (GL_LINE_STRIP, 0, NumVertices);
    glDrawArrays (GL_TRIANGLES, 0, NumVertices);
    glFlush ();
}